实践篇03--Spring Cloud Eureka

Author Avatar
子语 2018 - 01 - 15
  • 在其它设备中阅读本文章

Spring Cloud EurekaSpring Cloud Netflix微服务套件中的一部分,它基于Netflix Eureka的二次封装,主要是负责完成微服务架构的服务治理能力。我们只需通过简单引入依赖和注解配置就能使Spring Boot构建的微服务应用轻松地与Eureka服务治理体系进行整合。下面将构建用于服务治理的基础设施:

  • 构建服务注册中心
  • 服务注册与服务发现
  • Eureka的基础架构
  • Eureka的服务治理机制
  • Eureka的配置

服务治理

服务治理是微服务架构中最为核心和基础的模块,主要用于实现各个微服务实例的自动化注册与发现。随着业务的发展、功能的复杂、微服务应用的增加,静态配置维护难度会递增,且面对不断发展的业务,集群规模、服务位置和命名都有可能改变,手工维护的方式容易导致错误和冲突。

因此为了解决微服务架构中实例维护问题,出现了许多服务治理框架和产品,他们的实现主要都围绕着服务注册与服务发现机制来实现对微服务应用实例的自动化管理。

  • 服务注册:在服务治理框架中,会构建一个注册中心,每个服务向注册中心登记自己提供的服务,将主机、端口号、通信协议等信息告知。注册中心会在服务启动和注册后,维护一个服务清单,并以心跳的方式检测清单中的服务是否可用,若不可用则剔除,以达到排除故障的效果。
  • 服务发现:由于在服务治理框架下运作,服务间的调用不再通过指定具体的实例地址来发现,而是通过向服务名发起请求调用实现。所以,服务调用放在调用服务提供方接口时,并不知道具体的服务实例位置。因此调用发需要向服务注册中心咨询服务,并获取所有的服务的实例清单,以实现对具体服务实例的访问。

Netflix Eureka

Spring Cloud Eureka,使用Netflix Eureka实现服务注册与发现,既包含服务端组件,也包含客户端组件,提供了完备的RESTful API,支持将非Java语言构建的微服务纳入其服务治理体系中。

Eureka服务端,也称为服务注册中心,支持高可用配置,依托于强一致性提供良好的服务实例可用性,可以应对多重不同的故障场景。当Eureka以集群模式部署,集群中有分片出现故障时,Eureka会转入自动保护模式。它允许分片故障期间继续提供服务的发现和注册,当故障分片回复运行时,集群中其它分片会它们的状态再次同步回来。

Eureka客户端,主要处理服务的注册与发现。客户端服务通过注解和参数配置的方式,嵌入到客户端应用程序的代码中,当应用程序运行时,客户端向注册中心注册自身的提供的服务,并周期性地发送心跳来更新它的服务租约。同时它也能从服务端查询当前注册的服务信息并将它们缓存到本地并周期地刷新服务状态。

搭建服务注册中心

创建一个Spring Boot工程enreka-server,在pom.xml中引入相关依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>com.example</groupId>
   <artifactId>eureka-server</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>eureka-server</name>
   <description>Demo project for Spring Boot</description>

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.5.9.RELEASE</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>

   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
      <spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
   </properties>

   <!-- eureka服务依赖 -->
   <dependencies>
      <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-eureka-server</artifactId>
      </dependency>
   </dependencies>

   <dependencyManagement>
      <dependencies>
         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
         </dependency>
      </dependencies>
   </dependencyManagement>

   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>

</project>

在项目启动器中添加@EnableEurekaServer注解启动一个服务注册中心提供给其他应用进行对话。

package com.example.eurekaserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
   public static void main(String[] args) {
      SpringApplication.run(EurekaServerApplication.class, args);
   }
}

默认情况下,该服务注册中心也会将自己作为客户端进行注册。可在配置文件application.yml中禁用该项:

spring:
  application:
    name: Eureka-Server
server:
  port: 10000
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false # 不向注册中心注册自己
    fetch-registry: false  # 注册中心的职责是维护服务实例,不需要检索服务

启动应用,并访问http://localhost:10000,界面如下:

无法加载

此时可见Instances currently registered with Eureka为空,这是因为还没有服务向注册中心注册。

构建服务

接下来构建一个微服务向服务中心注册自己。新建Spring Boot项目eureka-client,在pom.xml添加依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>com.example</groupId>
   <artifactId>eureka-client</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>eureka-client</name>
   <description>Demo project for Spring Boot</description>

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.5.9.RELEASE</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>

   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
      <spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
   </properties>

   <dependencies>
      <!-- 添加Eureka依赖 -->
      <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-eureka</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>

   <dependencyManagement>
      <dependencies>
         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
         </dependency>
      </dependencies>
   </dependencyManagement>

   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>

</project>

添加/hello接口,通过注入DiscoveryClient对象,在日志中输出服务的相关信息。

package com.example.eurekaclient;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Yov
 * @date 2018/1/14 20:10
 */
@RestController
public class IndexController {
    private final Logger logger = Logger.getLogger(getClass());

    @Autowired
    private DiscoveryClient discoveryClient; // 用于获取服务

    @GetMapping("/hello")
    public String index() {
        discoveryClient.getServices().forEach(id -> {
            discoveryClient.getInstances(id).forEach(instance -> {
                // 在日志中输出服务的相关信息
                logger.info("Hello, host:" + instance.getHost() +
                        ", server_id: " + instance.getServiceId());
            });
        });
        return "Hello World!";
    }
}

在程序入口添加@EnableDiscoveryClient注解,激活Eureka中的DiscoveryClient(自动化配置,创建DiscoveryClient接口针对Eureka的EnrekaDiscoveryClient实例),这样才能实现Controller中对服务信息的输出。

package com.example.eurekaclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class EurekaClientApplication {

   public static void main(String[] args) {
      SpringApplication.run(EurekaClientApplication.class, args);
   }
}

修改配置文件application.yml

spring:
  application:
    name: hello-service  # 为服务命名
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10000/eureka # 为服务指定注册中心
server:
  port: 10001

启动服务后,可以在eureka-client控制台看到如下信息:

Registering application hello-service with eureka with status UP
Saw local status change event StatusChangeEvent [timestamp=1516155585227, current=UP, previous=STARTING]
DiscoveryClient_HELLO-SERVICE/youw:hello-service:10001: registering service...
DiscoveryClient_HELLO-SERVICE/youw:hello-service:10001 - registration status: 204
Tomcat started on port(s): 10001 (http)
Updating port to 10001

当访问该服务时,会出现控制台输出如下信息:Hello, host:youw, server_id: HELLO-SERVICE

eureka-server控制台可看到如下信息:Registered instance HELLO-SERVICE/youw:hello-service:10001 with status UP (replication=false)。访问http://localhost:10000,此时可见Instances currently registered with Eureka为增加了一个服务:HELLO-SERVICE

高可用注册中心

在微服务这样分布式的环境中,需要充分考虑发生故障的情况,所以在生产环节需要对各个组件进行高可用部署。Eureka Server充分考虑了这一点,每一个节点既可以是服务提供者,也可以是服务消费者。服务注册中心也可以向其他服务注册自身。

新建一个项目eureka-center,作为另一个服务注册中心,参照enreka-server,但要修改application.qml文件:

server:
  port: 20000
spring:
  application:
    name: eureka-server
eureka:
  instance:
    hostname: center
  client:
    service-url:
      defaultZone: http://localhost:10000/eureka/ # 指向注册中心

修改eureka-serverapplication.yml启动:

spring:
  application:
    name: eureka-Server
server:
  port: 10000
eureka:
  instance:
    hostname: localhost
  client:
    service-url:
      defaultZone: http://center:20000/eureka/ # 指向注册中心

修改eureka-clientapplication.yml

spring:
  application:
    name: hello-service  # 为服务命名
eureka:
  client:
    service-url:
      # 同时向两个注册中心注册自己
      defaultZone: http://localhost:10000/eureka/,http://center:20000/eureka/
server:
  port: 10001

其实启动三个应用程序,访问两个注册中心,均能看到:

无法加载

此时两个注册中心即使有一个中断了,在另一个注册中心的服务依然可以访问hello-server

This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://yov.oschina.io/article/微服务/Micro Service/实践篇03--Spring Cloud Eureka/